<?php

/**
 * This file is part of the Codizer package.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright   Copyright (c) 2010 Advisa (http://www.advisa.fr)
 * @author      Codizer Core Team <team@codizer.org>
 * @package     Codizer
 */

class f_file_Resolver
{
	private $directory;
	private $files;
	private $mask;
	private $recursive = false;

	private static $instance = null;

	/**
	 * Constructor
	 */
	private function __construct() {}

	/**
	 * @return f_file_Resolver
	 */
	public static function getInstance()
	{
		if (is_null(self::$instance))
		{
			self::$instance = new self();
		}
		return self::$instance;
	}
	
	/**
	 * Resolve a path
	 * ex : resolve("home","web","www","index.php");
	 * return : "/home/web/www/index.php"
	 *
	 * @return string
	 */
	public static function resolvePath()
	{
		$args = func_get_args();
		
		$path = realpath(implode(DS, $args));
		
		if($path != false)
		{
			return $path;
		}
		
		return null;
	}

    public function getOverload($filePath)
    {
        $path = str_replace(MODULES_DIR, '', $filePath);

        $webappPath = WEBAPP_MODULES_DIR.$path;

        return is_file($webappPath) ? $webappPath : $filePath;
    }

    /**
     * Get module name from a file path
     *
     * @example getModuleNameFromFilePath('/home/web/pagizer/modules/myModule/config/targets.xml') = > 'myModule'
     *
     * @param string $filePath
     * @return string
     */
    public static function getModuleNameFromFilePath($filePath)
    {
        $path = explode(DS, $filePath);

        $index = array_search('modules', $path);

        return $path[$index+1];
    }

    /**
     * Get a file path in a module
     *
     * @example getModuleFilePath("website", "config/blocks.xml");
     *
     * @param string $moduleName
     * @param string $filePathInModule
     * @return string|null
     */
    public static function getModuleFilePath($moduleName, $filePathInModule)
    {
        $firstChar = substr($filePathInModule, 0, 1);
        if($firstChar == DS)
        {
            $filePathInModule = substr($filePathInModule, 1);
        }

        $path = $moduleName.DS.$filePathInModule;

        if(is_file(WEBAPP_MODULES_DIR.DS.$path))
        {
            return realpath(WEBAPP_MODULES_DIR.DS.$path);
        }

        if(is_file(MODULES_DIR.DS.$path))
        {
            return realpath(MODULES_DIR.DS.$path);
        }

        return null;
    }
	
	/**
	 * Get a module realpath
	 *
	 * @param string $moduleName
	 * @return string
	 */
	public static function getModulePath($moduleName)
	{
		if(defined("WEBAPP_MODULES_DIR"))
		{
			if(is_dir(realpath(WEBAPP_MODULES_DIR.DS.$moduleName)))
			{
				return realpath(WEBAPP_MODULES_DIR.DS.$moduleName);
			}
		}
		else
		{
			throw new Exception("Cannot load module path, WEBAPP_MODULES_DIR is not defined");
		}
				
		if(defined("MODULES_DIR"))
		{
			if(is_dir(realpath(MODULES_DIR.DS.$moduleName)))
			{
				return realpath(MODULES_DIR.DS.$moduleName);
			}
			else
			{
				throw new Exception("Cannot load module path, directory doesn't exist");
			}
		}
		else
		{
			throw new Exception("Cannot load module path, MODULES_DIR is not defined");
		}
	}	
	
	
	/**
	 * Set the directory to use for finding file(s)
	 * ex : "/home/web/www"
	 *
	 * @param string $directoryName
	 * @return f_file_Resolver
	 */
	public function fromDirectory($directoryName)
	{
		$this->directory = $directoryName;
		return $this;
	}

	/**
	 * Set the mask to use to find file(s)
	 *
	 * @param string $mask
	 * @return f_file_Resolver
	 */
	public function setMask($mask)
	{
		$this->mask = $mask;
		return $this;
	}

	/**
	 * Seeking in recursive mode ?
	 *
	 * @param boolean $mode
	 * @return f_file_Resolver
	 */
	public function setRecursive($mode)
	{
		if(is_bool($mode))
		{
			$this->recursive = $mode;
		}
		return $this;
	}

	/**
	 * Get finded files path Array
	 *
	 * @return array<string>
	 */
	public function find()
	{
		$this->getResult();

		return $this->files;
	}

	/**
	 * Get the file path
	 * NB : no recursive mode possible
	 *
	 * @param string $fileName
	 * @return string
	 */
	public function getFilePath($fileName)
	{
		$this->setMask($fileName);
		$this->recursive = false;
		$this->getResult();
		return end($this->files);
	}

	/**
	 * Get the file object
	 *
	 * @param string $fileName
	 * @return f_file_File
	 */
	public function getFileObject($fileName)
	{
		$this->setMask($fileName);
		$this->recursive = false;
		$this->getResult();
		return f_file_File::getInstance(end($this->files));
	}

    /**
     * Get the filePath from the className
     * @param string $className
     * @return string
     */
    public function getFilePathByClassName($className)
    {
        $args = explode('_', $className);

        $firstChar = $args[0];

        switch ($firstChar)
        {
            // if first character is "f", class is in the FRAMEWORK_HOME folder
            case "f":
                $directory = FRAMEWORK_HOME;
            break;

            case "m":
                $directory = MODULES_DIR;
            break;

            case "w":
                $directory = WEBAPP_MODULES_DIR;
            break;
        }

        unset($args[0]);

        // set the class file path
        $file = $directory.DS.implode(DS, $args).".class.php";

        return $file;
    }

    /**
     * Get the className from the FilePath
     * @param string $filePath
     * @return string
     */
    public function getClassNameByFilePath($filePath)
    {
        $filePath = $this->getOverload($filePath);
        $args = explode(DS, $filePath);
        $s = "m";
        $word = "modules";
        
        if(in_array("webapp", $args))
        {
            $s = "w";
        }
        else if(in_array("codizer", $args))
        {
            $s = "f";
            $word = "codizer";
        }

        $key = array_search($word, $args);
        $args = array_splice($args, $key+1);

        $args[count($args)-1] = substr(end($args), 0, -10);

        $className = $s."_".join("_", $args);

        if(class_exists($className))
        {
            return $className;
        }
        else
        {
            throw new Exception("The class " . $className . " does not exist.");
        }
    }

	/**
	 * Find all files
	 *
	 * @return f_file_Resolver
	 */
	private function getResult()
	{
		if(is_null($this->mask)) $this->mask = "*.*";
		
		if($this->recursive == false)
		{
			$this->files = glob(realpath($this->directory).DS.$this->mask);
		}
		else
		{
			$this->files = $this->getFiles($this->getDirs($this->directory), $this->mask);
		}
	}

	/**
	 * Get dirs recursively
	 *
	 * @param string $path
	 * @return array
	 */
	private function getDirs($path = '.')
	{
		$list = array();
		$not_to_explore = array('.', '..', '.svn');

		// @ is used to avoid errors on symbolic links
		if(@is_dir(realpath($path)))
		{
			$list[] = realpath($path);
			$dir = opendir(realpath($path));
			while($item_dir = readdir($dir))
			{
				if(!in_array($item_dir, $not_to_explore))
				{
					if(is_dir($path.DS.$item_dir))
					{
						$tmp = $this->getDirs($path.DS.$item_dir);
						$list = array_merge($list,$tmp);
					}
				}
			}
			closedir($dir);
		}
		return $list;
	}

	/**
	 * Get files from a dir array
	 *
	 * @param array $dirs
	 * @param string $mask
	 * @return array
	 */
	private function getFiles($dirs, $mask)
	{
		$result = array();
		foreach ($dirs as $dir)
		{
			$files = glob($dir.DS.$mask);

			if(is_array($files) && count($files) > 0)
			{
				foreach ($files as $file)
				{
                    
					$result[] = realpath($file);
				}
			}
		}
		return $result;
	}

    public static function getAllFiles($dir, $excludeExt = array())
    {
        $result = array();

        $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir), RecursiveIteratorIterator::SELF_FIRST);

        foreach ($iterator as $file)
        {
            /* @var $file SplFileInfo */
            if($file->isFile())
            {
                $finfo = pathinfo($file->getFilename(), PATHINFO_EXTENSION);
                if($finfo == '')  $finfo = pathinfo($file->getFilename(), PATHINFO_BASENAME);
                if(!in_array($finfo, $excludeExt))
                {
                    if(!strstr($file->getPathname(), '.svn'))
                    {
                        $result[] = $file->getRealPath();
                    }
                }
            }
        }

        return $result;
    }

    /**
     * Get files from modules directory and webapp directory
     *
     * @param string $moduleName
     * @param string $mask
     * @return array
     */
    public function getModuleFiles($moduleName, $mask)
    {
        $moduleFiles = $this->fromDirectory(MODULES_DIR.DS.$moduleName)->setMask($mask)->find();
        $webappFiles = $this->fromDirectory(WEBAPP_MODULES_DIR.DS.$moduleName)->setMask($mask)->find();

        foreach($moduleFiles as $k => $file)
        {
            $filePath = str_replace(MODULES_DIR, '', $file);

            if(file_exists(WEBAPP_MODULES_DIR.$filePath))
            {
                unset($moduleFiles[$k]);
            }
        }

        return array_merge($moduleFiles, $webappFiles);
    }
}